//
//  SLClassInfo.m
//  Objective-C
//
//  Created by S_Line on 2018/9/20.
//  Copyright © 2018年 S_Line. All rights reserved.
//

#import "SLClassInfo.h"

SLEncodingType SLEncodingGetType(const char *typeEncoding) {
    char *type = (char *)typeEncoding;
    if (!type) {
        return SLEncodingTypeUnknown;
    }
    size_t len = strlen(type);
    if (len == 0) {
        return SLEncodingTypeUnknown;
    }
    SLEncodingType qualifier = 0;
    BOOL prefix = true;
    while (prefix) {
        switch (*type) {
            case 'r': {
                qualifier |= SLEncodingTypeQualifierConst;
                type++;
                } break;
            case 'n': {
                qualifier |= SLEncodingTypeQualifierIn;
                type++;
            } break;
            case 'N': {
                qualifier |= SLEncodingTypeQualifierInout;
                type++;
            } break;
            case 'o': {
                qualifier |= SLEncodingTypeQualifierOut;
                type++;
            } break;
            case 'O': {
                qualifier |= SLEncodingTypeQualifierBycopy;
                type++;
            } break;
            case 'R': {
                qualifier |= SLEncodingTypeQualifierByref;
                type++;
            } break;
            case 'V': {
                qualifier |= SLEncodingTypeQualifierOneway;
                type++;
            } break;
            default: {
                prefix = false;
            } break;
        }
    }
    
    len = strlen(type);
    if (len == 0) {
        return SLEncodingTypeUnknown | qualifier;
    }
    
    switch (*type) {
        case 'v': return SLEncodingTypeVoid | qualifier;
        case 'B': return SLEncodingTypeBool | qualifier;
        case 'c': return SLEncodingTypeInt8 | qualifier;
        case 'C': return SLEncodingTypeUInt8 | qualifier;
        case 's': return SLEncodingTypeInt16 | qualifier;
        case 'S': return SLEncodingTypeUInt16 | qualifier;
        case 'i': return SLEncodingTypeInt32 | qualifier;
        case 'I': return SLEncodingTypeUInt32 | qualifier;
        case 'l': return SLEncodingTypeInt32 | qualifier;
        case 'L': return SLEncodingTypeUInt32 | qualifier;
        case 'q': return SLEncodingTypeInt64 | qualifier;
        case 'Q': return SLEncodingTypeUInt64 | qualifier;
        case 'f': return SLEncodingTypeFloat | qualifier;
        case 'd': return SLEncodingTypeDouble | qualifier;
        case 'D': return SLEncodingTypeLongDouble | qualifier;
        case '#': return SLEncodingTypeClass | qualifier;
        case ':': return SLEncodingTypeSEL | qualifier;
        case '*': return SLEncodingTypeCString | qualifier;
        case '^': return SLEncodingTypePointer | qualifier;
        case '[': return SLEncodingTypeCArray | qualifier;
        case '(': return SLEncodingTypeUnion | qualifier;
        case '{': return SLEncodingTypeStruct | qualifier;
        case '@': {
            if (len == 2 && *(type + 1) == '?') {
                return SLEncodingTypeBlock | qualifier;
            } else {
                return SLEncodingTypeObject | qualifier;
            }
        }
        default: return SLEncodingTypeUnknown | qualifier;
    }
}

@implementation SLClassIvarInfo

- (instancetype)initWithIvar:(Ivar)ivar {
    if (!ivar) {
        return nil;
    }
    
    self = [super init];
    _ivar = ivar;
    const char *name = ivar_getName(ivar);
    if (name) {
        _name = [NSString stringWithUTF8String:name];
    }
    _offset = ivar_getOffset(ivar);
    const char *typeEncoding = ivar_getTypeEncoding(ivar);
    if (typeEncoding) {
        _typeEncoding = [NSString stringWithUTF8String:typeEncoding];
        _type = SLEncodingGetType(typeEncoding);
    }
    
    return self;
}

@end

@implementation SLClassMethodInfo

- (instancetype)initWithMethod:(Method)method {
    if (!method) {
        return nil;
    }
    self = [super init];
    
    _method = method;
    _sel = method_getName(method);
    _imp = method_getImplementation(method);
    const char *name = sel_getName(_sel);
    if (name) {
        _name = [NSString stringWithUTF8String:name];
    }
    const char *typeEncoding = method_getTypeEncoding(method);
    if (typeEncoding) {
        _typeEncoding = [NSString stringWithUTF8String:typeEncoding];
    }
    char * returnType = method_copyReturnType(method);
    if (returnType) {
        _returnTypeEncoding = [NSString stringWithUTF8String:returnType];
        free(returnType);
    }
    unsigned int argumentCount = method_getNumberOfArguments(method);
    if (argumentCount > 0) {
        NSMutableArray *argumentTypes = [NSMutableArray new];
        for (unsigned int i = 0; i < argumentCount; i++) {
            char *argumentType = method_copyArgumentType(method, i);
            NSString *type = argumentType ? [NSString stringWithUTF8String:argumentType] : nil;
            [argumentTypes addObject:type ? type : @""];
            if (argumentType) {
                free(argumentType);
            }
        }
        _argumentTypeEncodings = argumentTypes;
    }
    
    return self;
}

@end

@implementation SLClassPropertyInfo

- (instancetype)initWithProperty:(objc_property_t)property {
    if (!property) {
        return nil;
    }
    
    self = [super init];
    
    _property = property;
    const char *name = property_getName(property);
    if (name) {
        _name = [NSString stringWithUTF8String:name];
    }
    
    SLEncodingType type = 0;
    unsigned int attrCount;
    objc_property_attribute_t *attrs = property_copyAttributeList(property, &attrCount);
    
    return self;
}

@end

@implementation SLClassInfo

@end
